/** windows平台utf8/ansi/unicode互转
 * @file strconv.hpp
 * @author Kiven Lee
 * @version 1.0
 * @date 2021-11-13
 *
 * @copyright Kivensoft (c) 2018 - 2021
 *
 */

#pragma once

#include <cstddef>
#include <windows.h>

namespace kwin {

    template<typename CHAR_T, size_t SIZE>
    struct basic_chars_t {

        typedef CHAR_T value_type;

        size_t      _size;
        size_t      _cap;
        value_type* _data;
        value_type  _buf[SIZE];

        basic_chars_t(): _size(0), _data(_buf) {}
        ~basic_chars_t() { if (_data != _buf) delete[] _data; }

        value_type* data() { return _data; }
        const value_type* c_str() { return data(); }

        size_t length() { return size(); }
        size_t size() { return _size; }
    };

    template<size_t SIZE = 256>
    struct ansi_chars_t : public basic_chars_t<char, SIZE> {
        using basic_chars_t<char, SIZE>::_size;
        using basic_chars_t<char, SIZE>::_data;

        wchar_t _wbuf[SIZE];

        ansi_chars_t() {}

        ansi_chars_t(const char* u8_str) {
            if (!u8_str) return;

            size_t w_size = ::MultiByteToWideChar(CP_UTF8, 0, u8_str, -1, nullptr, 0);
            wchar_t *work = w_size <= SIZE ? _wbuf : new wchar_t[w_size];
            ::MultiByteToWideChar(CP_UTF8, 0, u8_str, -1, work, w_size);

            _size = ::WideCharToMultiByte(CP_ACP, 0, work, -1, nullptr, 0, nullptr, nullptr);
            if (_size > SIZE) _data = new char[_size];
            ::WideCharToMultiByte(CP_ACP, 0, work, -1, _data, _size, nullptr, nullptr);

            if (work != _wbuf) delete[] work;
        }

        ansi_chars_t(const wchar_t* str) {
            if (!str) return;

            _size = ::WideCharToMultiByte(CP_ACP, 0, str, -1, nullptr, 0, nullptr, nullptr);
            if (_size > SIZE) _data = new char[_size];
            ::WideCharToMultiByte(CP_ACP, 0, str, -1, _data, _size, nullptr, nullptr);
        }
    };

    template<size_t SIZE = 256>
    struct utf8_chars_t : public basic_chars_t<char, SIZE> {
        using basic_chars_t<char, SIZE>::_size;
        using basic_chars_t<char, SIZE>::_data;

        wchar_t _wbuf[SIZE];

        utf8_chars_t(): basic_chars_t<char, SIZE>() {}

        utf8_chars_t(const char* ansi_str): utf8_chars_t() {
            if (!ansi_str) return;

            size_t w_size = ::MultiByteToWideChar(CP_ACP, 0, ansi_str, -1, nullptr, 0);
            WCHAR* work = w_size <= SIZE ? _wbuf : new WCHAR[w_size];
            ::MultiByteToWideChar(CP_ACP, 0, ansi_str, -1, work, w_size);

            _size = ::WideCharToMultiByte(CP_UTF8, 0, work, -1, nullptr, 0, nullptr, nullptr);
            if (_size > SIZE) _data = new char[_size];
            ::WideCharToMultiByte(CP_UTF8, 0, work, -1, _data, _size, nullptr, nullptr);

            if (work != _wbuf) delete[] work;
        }

        utf8_chars_t(const wchar_t* str): utf8_chars_t() {
            if (!str) return;

            _size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, nullptr, nullptr);
            if (_size > SIZE) _data = new char[_size];
            ::WideCharToMultiByte(CP_UTF8, 0, str, -1, _data, _size, nullptr, nullptr);
        }
    };

    template<size_t SIZE = 250, bool IS_UTF8 = false>
    struct wide_chars_t : public basic_chars_t<wchar_t, SIZE> {
        using basic_chars_t<wchar_t, SIZE>::_size;
        using basic_chars_t<wchar_t, SIZE>::_data;

        wide_chars_t(): basic_chars_t<wchar_t, SIZE>() {}

        wide_chars_t(const char* str): wide_chars_t() {
            if (!str) return;

            UINT cp = IS_UTF8 ? CP_UTF8 : CP_ACP;
            _size = ::MultiByteToWideChar(cp, 0, str, -1, nullptr, 0);
            if (_size > SIZE) _data = new wchar_t[_size];
            ::MultiByteToWideChar(cp, 0, str, -1, _data, _size);
        }
    };
} // namespace kwin